/* WiFi station Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_eap_client.h"
#include "esp_event.h"
#include "esp_log.h"
// #include "esp_system.h"
// #include "nvs_flash.h"
// #include "esp_netif.h"
#include "esp_smartconfig.h"
// #include "esp_mac.h"
// #include "lwip/err.h"
// #include "lwip/sys.h"
// #include "esp_sleep.h"
#include "station.h"
#include "nvs_value.h"
#include "main.h"
#include "selftest_mode.h"
#include "mqtt_event.h"
#include "gatt_server.h"
#include "touch_board.h"
#include "ws2812.h"
#include "lamp_ctrl.h"
#include "h03_config.h"

/* The examples use WiFi configuration that you can set via project configuration menu

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/

#if CONFIG_ESP_WIFI_AUTH_OPEN
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN
#elif CONFIG_ESP_WIFI_AUTH_WEP
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP
#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK
#endif

/* FreeRTOS event group to signal when we are connected*/
EventGroupHandle_t s_wifi_event_group;
static wifi_config_t wifi_config;

esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;

// static uint8_t ssid[33] = { 0 };
// static uint8_t password[65] = { 0 };
static uint8_t rvd_data[33] = { 0 };

/* The event group allows multiple bits for each event, but we only care about two events:
 * - we are connected to the AP with an IP
 * - we failed to connect after the maximum amount of retries */

static const char *TAG = "[STATION]";

static uint16_t s_retry_num = 0;
static uint16_t maximum_retry;

void smartconfig_example_task(void * parm);

int8_t get_Wifi_RSSI(void)
{
    wifi_ap_record_t wifi_info;
    esp_wifi_sta_get_ap_info(&wifi_info);

    ESP_LOGI(TAG, "Current WiFi RSSI: %d", wifi_info.rssi);
    return wifi_info.rssi;
}

static void wifi_event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        if(system_state.main == SMARTCONFIG){
            xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
        }else{
            esp_wifi_connect();
            system_state.wifi = wifi_TRYING;
            s_retry_num++;
            ESP_LOGI(TAG, "%d/%d times", s_retry_num, maximum_retry);
            ble_update_wifi_state(system_state.wifi, notify_state, gatt_set_wifi_flag);
            ESP_LOGI(TAG, "Try to connect ---");
        }
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        if(wifi_stop_flag != 1){
            if (s_retry_num < maximum_retry) {
                ESP_LOGE(TAG,"connect to the AP fail");
                xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
                ESP_LOGE(TAG, "wait...");
                if(s_retry_num >= 3){
                    for(uint8_t i = 0; i < 8; i++) {
                        vTaskDelay(1000 / portTICK_PERIOD_MS);
                    }
                    if(s_retry_num == 6){
                        nvs_record_error(1, 8);
                    }
                }else if(s_retry_num >= 2){
                    for(uint8_t i = 0; i < 3; i++) {
                        vTaskDelay(1000 / portTICK_PERIOD_MS);
                    }
                    if(s_retry_num == 6){
                        nvs_record_error(1, 8);
                    }
                }else if(s_retry_num == 0){
                    vTaskDelay(1000 / portTICK_PERIOD_MS);
                }
                esp_wifi_connect();
                system_state.wifi = wifi_TRYING;
                ble_update_wifi_state(system_state.wifi, notify_state, gatt_set_wifi_flag);
                s_retry_num++;
                ESP_LOGI(TAG, "%d/%d times", s_retry_num, maximum_retry);
                ESP_LOGI(TAG, "retry to connect to the AP");
            }else{
                if(system_state.main == STATION || system_state.main == MQTT_DISCONNECTED){
                    s_retry_num = 0;
                    esp_wifi_connect();
                }
                system_state.wifi = wifi_FAIL;
                ble_update_wifi_state(system_state.wifi, notify_state, gatt_set_wifi_flag);
                xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
                gatt_set_wifi_flag = 0;
            }
        }
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        system_state.wifi = wifi_SUCCESS;
        ble_update_wifi_state(system_state.wifi, notify_state, gatt_set_wifi_flag);
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
        gatt_set_wifi_flag = 0;
        if(system_state.main == MQTT_DISCONNECTED){
            ESP_LOGI(TAG, "esp_mqtt_client_reconnect");
            mqtt_reconnect();
        }
        nvs_record_error(0, 8);
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
        ESP_LOGI(TAG, "Scan done");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
        ESP_LOGI(TAG, "Found channel");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
        ESP_LOGI(TAG, "Got SSID and password");
        
        system_state.main = STATION;
        led_notify = BLUE_NOTIFICATION;
        event_flag.led_break = 1;
        indicator_updata();

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true) {
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }

        memcpy(device_attr.wifi_param.wifi_ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(device_attr.wifi_param.wifi_password, evt->password, sizeof(evt->password));

        ESP_LOGD(TAG, "SSID:%s", device_attr.wifi_param.wifi_ssid);
        ESP_LOGD(TAG, "PASSWORD:%s", device_attr.wifi_param.wifi_password);
        paramenter_setting_str("wifi_ssid",(char *)device_attr.wifi_param.wifi_ssid);
        paramenter_setting_str("wifi_password",(char *)device_attr.wifi_param.wifi_password);
        paramenter_setting_str("initial","INIT_OK");
        if (evt->type == SC_TYPE_ESPTOUCH_V2) {
            ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
            ESP_LOGI(TAG, "RVD_DATA:");
            if(device_attr.debug_mode == 2){
                for (int i=0; i<33; i++) {
                    printf("%02x ", rvd_data[i]);
                }
                printf("\n");
            }
        }

        ESP_ERROR_CHECK( esp_wifi_disconnect() );
        ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
        esp_wifi_connect();
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

void gatt_set_wifi(void)
{
    ESP_LOGI(TAG, "gatt set wifi");
    if(system_state.main == SMARTCONFIG){
        xEventGroupSetBits(s_wifi_event_group, GATT_SET_WIFI);
        vTaskDelay(200 / portTICK_PERIOD_MS);
    }
    system_state.main = STATION;

    wifi_stop_flag = 1;
    ESP_ERROR_CHECK(esp_wifi_disconnect());
    vTaskDelay(200 / portTICK_PERIOD_MS);
    ESP_ERROR_CHECK(esp_wifi_stop());

    maximum_retry = SETWIFI_MAXIMUM_RETRY;
    s_retry_num = 0;

    set_wifi_config(0);
    paramenter_setting_str("wifi_ssid",(char *)device_attr.wifi_param.wifi_ssid);
    paramenter_setting_str("wifi_password",(char *)device_attr.wifi_param.wifi_password);
    device_attr.initial = 2;
    paramenter_setting_u8("initial", device_attr.initial);
    wifi_stop_flag = 0;
    ESP_ERROR_CHECK( esp_wifi_start() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");
}

void smartconfig_example_task(void * parm)
{
    ESP_LOGI(TAG, "smartconfig task");

    EventBits_t uxBits;
    ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
    while (1) {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT | ESPTOUCH_DONE_BIT | GATT_SET_WIFI, true, false, portMAX_DELAY);
        if(uxBits & WIFI_CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");
            s_retry_num = 0;
            maximum_retry = NORMAL_MAXIMUM_RETRY;
            timer_count.ble_stop = 10;
        }
        if(uxBits & ESPTOUCH_DONE_BIT) {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            ESP_ERROR_CHECK( esp_event_handler_unregister(SC_EVENT,
                                                                ESP_EVENT_ANY_ID,
                                                                wifi_event_handler) );
            break;
        }
        if (uxBits & WIFI_FAIL_BIT) {
            // ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                    // EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
            ESP_LOGI(TAG, "Failed to connect to SSID:%s",
                device_attr.wifi_param.wifi_ssid);
            restart_os();
        }
        if(uxBits & GATT_SET_WIFI) {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            ESP_ERROR_CHECK( esp_event_handler_unregister(SC_EVENT,
                                                                ESP_EVENT_ANY_ID,
                                                                wifi_event_handler) );
            break;
        }
    }
    vTaskDelete(NULL);
}

void smartconfig(void)
{
    ESP_LOGI(TAG,"start wifi smartconfig!");

    system_state.main = SMARTCONFIG;
    event_flag.led_break = 1;
    maximum_retry = SETWIFI_MAXIMUM_RETRY;
    s_retry_num = 0;
    ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL) );
    xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT | ESPTOUCH_DONE_BIT | GATT_SET_WIFI );
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    
    ESP_ERROR_CHECK( esp_wifi_start() );

    xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT,
                        true, false, portMAX_DELAY);
}

void set_wifi_config(bool scan_need)
{
    bzero(&wifi_config, sizeof(wifi_config_t));
    if(scan_need == 1){
        system_state.main = FAST_SCAN;
        maximum_retry = FAST_SCAN_ESP_MAXIMUM_RETRY;
        memcpy(wifi_config.sta.ssid, WRITE_ID_SSID, strlen(WRITE_ID_SSID));
        memcpy(wifi_config.sta.password, WRITE_ID_PWD, strlen(WRITE_ID_PWD));
        wifi_config.sta.scan_method = WIFI_FAST_SCAN;
        wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL;
        wifi_config.sta.threshold.rssi = SELFTEST_RSSI;
    }else if(scan_need == 0){
        system_state.main = STATION;
        maximum_retry = NORMAL_MAXIMUM_RETRY;
        memcpy(wifi_config.sta.ssid, device_attr.wifi_param.wifi_ssid, strlen(device_attr.wifi_param.wifi_ssid));
        memcpy(wifi_config.sta.password, device_attr.wifi_param.wifi_password, strlen(device_attr.wifi_param.wifi_password));
    }
    if(top_led_mode == 1){
        event_flag.led_break = 1;
    }
    wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA_PSK;
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
}

uint8_t station(bool scan_need)
{
    ESP_LOGI(TAG,"start wifi station!");

    uint8_t smartconfig_need = 0;

    s_retry_num = 0;

    set_wifi_config(scan_need);

    ESP_ERROR_CHECK( esp_wifi_start() );
    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT) {
        // ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                // EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
        if(system_state.main == FAST_SCAN){
            ESP_LOGI(TAG, "connected to ap SSID:%s", WRITE_ID_SSID);
            ESP_LOGI(TAG, "wifi done");
            selftest_init();
            while(lamp.onoff_status == 0){
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
            smartconfig_need = 0;
        }else{
            ESP_LOGI(TAG, "connected to ap SSID:%s", device_attr.wifi_param.wifi_ssid);
            smartconfig_need = 0;
        }
        s_retry_num = 0;
    } else if (bits & WIFI_FAIL_BIT) {
        // ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                // EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
        if(system_state.main == FAST_SCAN){
            ESP_LOGI(TAG, "Failed to connect to SSID:%s", WRITE_ID_SSID);
            ESP_ERROR_CHECK( esp_wifi_stop() );
            bzero(&wifi_config, sizeof(wifi_config_t));
            maximum_retry = SETWIFI_MAXIMUM_RETRY;
            smartconfig_need = 1;
        }else{
            ESP_LOGI(TAG, "Failed to connect to SSID:%s", device_attr.wifi_param.wifi_ssid);
            smartconfig_need = 0;
        }
        system_state.wifi = wifi_WAITING;
        ble_update_wifi_state(system_state.wifi, notify_state, gatt_set_wifi_flag);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
        smartconfig_need = 0;
    }
    return smartconfig_need;
    /* The event will not be processed after unregister */
    // ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    // ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    // vEventGroupDelete(s_wifi_event_group);
}

void initialise_wifi(void)
{
    ESP_LOGI(TAG, "initialise wifi");

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
    s_wifi_event_group = xEventGroupCreate();
    xEventGroupClearBits( s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT | ESPTOUCH_DONE_BIT | GATT_SET_WIFI | HTTPS_GET_BIT);

    ESP_ERROR_CHECK( esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK( esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        &instance_got_ip));
}
